unit Engine;

interface

uses
  SysUtils,
  ActiveX,
  Windows,
  Types,
  Messages,
  Forms,
  DirectDraw,
  DirectShow9,
  lua52,
  Debug,
  Classes,
  MMSystem,
  graphics,
  Head;

type

  TEngine_Music_Control = (EMC_none, EMC_restore, EMC_silent);

  //PPNGObject = ^TPNGObject;
  PPicture = ^TPicture;

  PDDSURFACEDESC2 = ^DDSURFACEDESC2;
  PDirectDrawSurface7 = ^IDirectDrawSurface7;

  TEngine = class
    wc : WNDCLASSEX;
    GameWindow: HWND;
    DDraw7: IDirectDraw7;
    ScreenSurface: IDirectDrawSurface7;
    ScreenBufSurface: IDirectDrawSurface7;
    DDSD: DDSURFACEDESC2;
    BUFDDSD: DDSURFACEDESC2;
    DdClipper: IDirectDrawClipper;
    ScreenData: array of byte;
    ScreenLpitch : integer;
    ScreenBufSize: integer;
    TimerID: Cardinal;
    hTimeEvent: Cardinal;

    Frequency: int64;
    WMode: Boolean;
    window_W: integer;
    window_H: integer;
    MaxFPS: integer;
    EngineShowCursor: boolean;
    windowcaption: string;
    CursorShow: boolean;

    pGraph: IGraphBuilder;
    pControl: IMediaControl;
    pMediaSeeking: IMediaSeeking; // 播放位置.
    pAudioControl: IBasicAudio; // 音量/平衡设置.

    VersionInformation: TOSVERSIONINFO;    //系统信息

    function Initial: integer;
    procedure Release;
    function WinInitial: integer;
    function InitialDDraw: integer;
    function InitialDShow: integer;
    procedure ReleaseDShow;
    procedure ReleaseDDraw;
    //function InitWindowlessVMR: integer;
    function PlayMovie(Filename: string): Longint;
    function ScreenLock: integer;
    function ScreenUnLock: integer;
    function ScreenBufLock: integer;
    function ScreenBufUnLock: integer;
    procedure SurfaceRestore;
    procedure Update;
    Constructor Create(WindowMode: Boolean; Width, Height: integer; Caption: String; ShowCursor: Boolean; FPS: integer); overload;

  const
    //MaxFPS: integer = 60;
    //window_W: integer = 800;
    //window_H: integer = 600;
    dwpp: integer = 32;
    lpszClassName: string = 'UpwindedGameEngine';
    //windowcaption: string = 'GameSword';
  public
    function BufTextOut(Str: String; x, y, Size, Col: integer): integer;
    procedure drawpictobuf(Pdata: Pbyte; datalen, x, y: integer); overload;
    procedure drawpictobuf(Filename: String; x, y: integer); overload;
    procedure drawpictobuf(APIC: PPicture; x, y: integer);   overload;
    function UpdateCursor(var x, y: integer): boolean;
    procedure initialalpha(DirectDsd: PDDSURFACEDESC2);
    procedure putpixel(DirectDsd: PDDSURFACEDESC2; x, y: integer; Color: Cardinal); overload;
    procedure putpixel(x, y: integer; Color: Cardinal); overload;
    procedure putBufpixel(x, y: integer; Color: Cardinal);
    function getpixel(DirectDsd: PDDSURFACEDESC2; x, y: integer): Cardinal; overload;
    function getpixel(x, y: integer): Cardinal; overload;
    function getBufpixel(x, y: integer): Cardinal;
    procedure Restore(Psurface: PDirectDrawSurface7);
    function CreateNewSurface(W, H: integer; var Psurface: PDirectDrawSurface7; var Newdesc: DDSURFACEDESC2): integer;
    procedure ReleaseSurface(Psurface: PDirectDrawSurface7);
    procedure putdatapixel(x, y: integer; Color: Cardinal);
    function CanContinue: boolean;
    function Getnow: integer;
    procedure DataToBuf;
    procedure BufToData;

  end;

  PEngine = ^TEngine;

function MainWndProc(winwnd: hWnd; msg: UINT; winWPARAM: wParam; winLPARAM: lParam ): LResult; stdcall;
procedure UpdateBounds(WH: Cardinal);
procedure TimerReady(HWND, nMsg, nTimerID, dwTime: Cardinal); stdcall;

var
  Paused: Boolean;
  Engine_MovPlaying: Boolean = false;
  Engine_MovStopKey: integer = VK_ESCAPE;
  Engine_WRect: Trect;
  Engine_CRect: Trect;
  Engine_MRect: TRect;
  Engine_BW, Engine_BH, Engine_CH: integer;

  //pWc: IVMRWindowlessControl;
  pVideoWindow: IVideoWindow; //设置播放窗体.

  Upwinded_EngineWindowMode: Boolean;
  NeedRestore: Boolean = false;
  CanRestore: Boolean = false;
  Engine_Active: Boolean = false;
  Engine_Music_Control: TEngine_Music_Control = EMC_none;

implementation

//窗体回调函数
function MainWndProc(winwnd: hWnd; msg: UINT; winWPARAM: wParam; winLPARAM: lParam ): LResult; stdcall;
var
  DC: HDC;
  //rect: ^Trect;
begin
  case msg of
    WM_ACTIVATEAPP:
      begin
        //Engine_Active := not Engine_Active;
        Engine_Active := Boolean(winWPARAM);
        if Engine_Active then
          Engine_Music_Control := EMC_restore
        else
          Engine_Music_Control := EMC_silent;
        if CanRestore and Engine_Active then
          NeedRestore := true;
        if Engine_Active and Engine_MovPlaying and Upwinded_EngineWindowMode then
        begin
          pVideoWindow.SetWindowposition(Engine_MRect.Left, Engine_MRect.Top, Engine_MRect.Right - Engine_MRect.Left, Engine_MRect.Bottom - Engine_MRect.Top)
        end;
        GameDebug.AddDebugInfo('ACTIVATEAPP:' + booltostr(Engine_Active, 'true', 'false'));
      end;
    WM_MOVE:
      begin
        Paused := true;
        updateBounds(Winwnd);
      end;
    WM_DESTROY:
      begin
        GameRun := false;
        if Engine_MovPlaying then
        begin
          Engine_MovPlaying := false;
        end;
      end;
    WM_INITMENUPOPUP:
      begin
        Paused := true;
      end;
    WM_KEYDOWN:
      begin
        if Engine_MovPlaying then
        begin
          if winWParam = Engine_MovStopKey then
          begin
            Engine_MovPlaying := false;
          end;
        end;
      end;
  end;
  result := DefWindowProc(winwnd, msg, winwParam, winlParam)
end;

procedure UpdateBounds(WH: Cardinal);
begin
  //
  if Upwinded_EngineWindowMode then
  begin
    GetWindowRect(WH, Engine_WRect);
    GetClientRect(WH, Engine_CRect);
    Engine_MRect := Engine_Crect;
    Engine_Crect.Left := Engine_CRect.Left + Engine_Wrect.Left + Engine_BW;
    Engine_Crect.Right := Engine_Wrect.Left + Engine_Crect.Right + Engine_BW;
    Engine_Crect.Top := Engine_Crect.Top + Engine_Wrect.Bottom - Engine_BH - Engine_Crect.Bottom;
    Engine_Crect.Bottom := Engine_Wrect.Bottom - Engine_BH;
  end;
end;

procedure TimerReady(HWND, nMsg, nTimerID, dwTime: Cardinal); stdcall;
begin
  //
  //GameTimerReady := true;
end;

function TEngine.Getnow: integer;
var
  now: int64;
begin
  QueryPerformanceCounter(now);
  result := Round(now * 1000 / frequency);
end;

Constructor TEngine.Create(WindowMode: Boolean; Width, Height: integer; Caption: String; ShowCursor: Boolean; FPS: integer);
begin
  //inherited;
  WMode := WindowMode;
  Upwinded_EngineWindowMode := WMode;
  Window_H := Height;
  Window_W := Width;
  windowcaption := Caption;
  EngineShowCursor := ShowCursor;
  MaxFPS := FPS;
  //Initial;
end;

//底层引擎初始化
function TEngine.Initial: integer;
var
  Windowrc: Trect;
begin
  //
  VersionInformation.dwOSVersionInfoSize := sizeof(VersionInformation);
  GetVersionEx(VersionInformation);
  GameDebug.AddDebugInfo('Create Timer!');
  hTimeEvent := CreateEvent(nil, false, false, nil);
  GameDebug.AddDebugInfo('Timer Event Created!');
  TimerID := timeSetEvent(1000 div MaxFps, 0, pointer(hTimeEvent), 0, TIME_CALLBACK_EVENT_SET or TIME_PERIODIC);
  GameDebug.AddDebugInfo('Timer Event Seted!');
  if (hTimeEvent = 0) or (TimerID = 0) then
  begin
    result := 4;
    GameDebug.AddDebugInfo('Create Timer Fail!');
    exit;
  end;

  QueryPerformanceFrequency(Frequency);
  GameDebug.AddDebugInfo('Query Performance Frequency!');
  //Settimer(GameWindow, TimerID, 1000 div MaxFPS, @TimerReady);
  //GameTimerReady := true;

  if (WinInitial <> 0) then
  begin
    //错误
    result := 2;
    exit;
  end;
  GameDebug.AddDebugInfo('Window Initialed!');

  if (InitialDDraw <> 0) then
  begin
    //错误
    result := 3;
    exit;
  end;

  {
  if InitialDShow <> 0 then
  begin
    result := 4;
    exit;
  end;
  GameDebug.AddDebugInfo('DirectShow Initialed!');}

  CanRestore := true;
  //ShowWindow(GameWindow, SW_SHOWDEFAULT);
  //UpdateWindow(GameWindow);
  result := 0;
end;

//引擎释放资源
procedure TEngine.Release;
begin
  //释放DirectDraw
  //ReleaseDShow;
  ReleaseDDraw;
  timeKillEvent(TimerID);
  if hTimeEvent <> 0 then
    closehandle(hTimeEvent);
end;

{function TEngine.InitWindowlessVMR: integer;
var
  pVmr: IBaseFilter;
  pConfig: IVMRFilterConfig;
begin
  if Failed(CoCreateInstance(CLSID_VideoMixingRenderer, nil, CLSCTX_INPROC, IID_IBaseFilter, pVmr)) then
  begin
    result := 1;
    exit;
  end;
  // Add the VMR to the filter graph.
  if Failed(pGraph.AddFilter(pVmr, 'Video Mixing Renderer')) then
  begin
    result := 2;
    pVmr._Release;
    exit;
  end;
  // Set the rendering mode.
  if Failed(pVmr.QueryInterface(IID_IVMRFilterConfig, pConfig)) then
  begin
    result := 3;
    pVmr._Release;
    exit;
  end
  else
  begin
    if Failed(pConfig.SetRenderingMode(VMRMode_Windowless)) then
    begin
      result := 4;
      pConfig._Release;
      pVmr._Release;
      exit;
    end
    else
    begin
      if Failed(pVmr.QueryInterface(IID_IVMRWindowlessControl, pWc)) then
      begin
        result := 5;
        pWc._Release;
        pConfig._Release;
        pVmr._Release;
        exit;
      end
      else
      begin
        if Failed(pWc.SetVideoClippingWindow(GameWindow)) then
        begin
          result := 6;
          pWc._Release;
          pConfig._Release;
          pVmr._Release;
          exit;
        end;
      end;
    end;
  end;
  pConfig._Release;
  pVmr._Release;
  result := 0;
end;}

function TEngine.InitialDShow: integer;
begin
  result := -1;

  if Failed(CoInitialize(nil)) then
  begin
    result := 1;
    exit;
  end;
  GameDebug.AddDebugInfo('CoInitialized!');

  //GameDebug.AddDebugInfo('Initial Dshow Control and Event!');
 { if Failed(CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, pGraph)) then
  begin
    result := 2;
    exit;
  end;

  if Failed(pGraph.QueryInterface(IID_IMediaControl, pControl)) then
  begin
    result := 3;
    exit;
  end;
  if Failed(pGraph.QueryInterface(IID_IMediaEvent, pEvent)) then
  begin
    result := 4;
    exit;
  end;
  if failed(pGraph.QueryInterface(IID_IMediaSeeking, pMediaSeeking)) then
  begin
    result := 5;
    exit;
  end;
  if failed(pGraph.QueryInterface(IID_IBasicAudio, pAudioControl)) then
  begin
    result := 6;
    exit;
  end;
  if failed(pGraph.QueryInterface(IID_IVideoWindow, pVideoWindow)) then
  begin
    result := 7;
    exit;
  end;  }

  {if InitWindowlessVMR <> 0 then
  begin
    result := 3;
    exit;
  end;  }

  result := 0;
end;

function TEngine.PlayMovie(Filename: string): Longint;
var
  evCode: Longint;
  TempRect: Trect;
  amp: msg;
  _current, _stop: LONGLONG;
begin
  Engine_MovPlaying := true;

  Engine_MovStopKey := VK_ESCAPE;
  if InitialDShow <> 0 then
  begin
    result := 1;
    exit;
  end;

  if Failed(CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, pGraph)) then
  begin
    result := 2;
    exit;
  end;
  if Failed(pGraph.QueryInterface(IID_IMediaControl, pControl)) then
  begin
    result := 3;
    exit;
  end;
  if failed(pGraph.QueryInterface(IID_IMediaSeeking, pMediaSeeking)) then
  begin
    result := 5;
    exit;
  end;
  if failed(pGraph.QueryInterface(IID_IBasicAudio, pAudioControl)) then
  begin
    result := 6;
    exit;
  end;
  if failed(pGraph.QueryInterface(IID_IVideoWindow, pVideoWindow)) then
  begin
    result := 7;
    exit;
  end;

  if Failed(pGraph.RenderFile(Pwidechar(UTF8Decode(Filename)), nil)) then
  begin
    GameDebug.AddDebugInfo('Render File Fail!');
    pVideoWindow.put_Visible(false);
    Engine_MovPlaying := false;
    result := -1;
    exit;
  end;

  pVideoWindow.put_Owner(GameWindow);
  pVideoWindow.put_windowstyle(WS_CHILD or WS_Clipsiblings);
  pVideoWindow.put_Visible(true);

  if WMode then
  begin
    pVideoWindow.HideCursor(false);
    GetClientRect(GameWindow, TempRect);
    pVideoWindow.SetWindowposition(TempRect.Left, TempRect.Top, TempRect.Right - TempRect.Left, TempRect.Bottom - TempRect.Top)
  end
  else
  begin
    pVideoWindow.HideCursor(true);
    if VersionInformation.dwMajorVersion < 6 then
    begin
      pVideoWindow.SetWindowposition(0, 0, Window_W, Window_H);
      DDraw7.SetCooperativeLevel(GameWindow, DDSCL_NORMAL);
      DDraw7.SetDisplayMode(window_w, window_h, dwpp, 0, 0);
    end
    else
    begin
      pVideoWindow.SetWindowposition(0, 0, Window_W, Window_H);
    end;
  end;

  pControl.Run;

  GameDebug.AddDebugInfo('Play Movie!');

  while Engine_MovPlaying do
  begin
    if (PeekMessage(amp, GameWindow, 0 , 0 , PM_REMOVE)) then
    begin
      // 消息转换
      TranslateMessage(amp);
      DispatchMessage(amp);
    end;
    pMediaSeeking.GetPositions(_current, _stop);
    if (_stop = 0) or (_stop = _current) then
    begin
      Engine_MovPlaying := false;
    end;
  end;

  pControl.Stop;

  if Assigned(pVideoWindow) then
  begin
    pVideoWindow := nil;
  end;
  if Assigned(pAudioControl) then
  begin
    pAudioControl := nil;
  end;

  if Assigned(pControl) then
  begin
    pControl := nil;
  end;
  if Assigned(pMediaSeeking) then
  begin
    pMediaSeeking := nil;
  end;
  if Assigned(pGraph) then
  begin
    pGraph := nil;
  end;

  ReleaseDShow;

  if not WMode then
  begin
    if VersionInformation.dwMajorVersion < 6 then
    begin
      DDraw7.SetCooperativeLevel(GameWindow, DDSCL_ALLOWREBOOT or DDSCL_ALLOWMODEX or DDSCL_FULLSCREEN or DDSCL_EXCLUSIVE);
      DDraw7.RestoreAllSurfaces;
    end
    else
    begin
      DDraw7.SetCooperativeLevel(GameWindow, DDSCL_ALLOWREBOOT or DDSCL_ALLOWMODEX or DDSCL_FULLSCREEN or DDSCL_EXCLUSIVE);
      DDraw7.SetDisplayMode(window_w, window_h, dwpp, 0, 0);
      DDraw7.RestoreAllSurfaces;
    end;
  end;

  result := evCode;
  Engine_MovPlaying := false;
end;

procedure TEngine.ReleaseDShow;
begin
  //
  //pWc._Release;
  //pControl._Release;
  //pEvent._Release();
  //pGraph._Release();
  CoUninitialize();
end;

function TEngine.CanContinue: boolean;
begin
  //tempint := TimerID;
  //temphandle := htimeevent;
  //htimeEvent := CreateEvent(nil, false, false, nil);
  //TimerID := TimeSetEvent((Frame + 1) * 1000 div MaxFps - Frame * 1000 div MaxFps, 0, pointer(hTimeEvent), 0, TIME_CALLBACK_EVENT_SET or TIME_PERIODIC);

  WaitForSingleObject(hTimeEvent, 1000 div MaxFps {(Frame + 1) * 1000 div MaxFps - Frame * 1000 div MaxFps});
  ResetEvent(hTimeEvent);
  result := true;
end;

//窗体初始化
function TEngine.WinInitial: integer;
var
  rc, rcwork: Trect;
  dwstyle: DWORD;
begin
  //
  GameDebug.AddDebugInfo('Get System Options!');
  Engine_BW := GetSystemMetrics(SM_CXFIXEDFRAME);
  Engine_BH := GetSystemMetrics(SM_CYFIXEDFRAME);
  Engine_CH := GetSystemMetrics(SM_CYCAPTION);

  wc.cbSize        := sizeof(wc);
  wc.lpszClassName := Pchar(lpszClassName);
  wc.lpfnWndProc   := @MainWndProc;
  wc.style         := CS_DBLCLKS or CS_OWNDC or CS_HREDRAW or CS_VREDRAW;
  wc.hInstance     := hInstance;
  wc.hIcon         := LoadIcon(hinstance, 'MAINICON');//LoadImage(0, pchar(ExtractFilePath(Paramstr(0)) + '\resource\game.ico'),IMAGE_ICON, 32, 32,  LR_LOADFROMFILE);    //LR_CREATEDIBSECTION or
  //wc.hIconSm       := 0;
  wc.hCursor       := 0;//LoadCursor(hinstance, IDC_ARROW);
  wc.hbrBackground := HBRUSH(GetStockObject(BLACK_BRUSH));
  wc.lpszMenuName  := Pchar(lpszClassName);
  wc.cbClsExtra    := 0;
  wc.cbWndExtra    := 0;

  GameDebug.AddDebugInfo('Register Class!');
  RegisterClassEx(wc);
  GameDebug.AddDebugInfo('Create Game Window');

  if WMode then
  begin
    Engine_Wrect.Left := (screen.Width - Window_W) div 2;
    Engine_Wrect.Top := (screen.Height - Window_H) div 2;
    Engine_Wrect.Right := Engine_Wrect.Left + Window_W;
    Engine_Wrect.Bottom := Engine_Wrect.Top + Window_H;

    GameWindow := CreateWindowEx(0, Pchar(StrToAnsi(lpszClassName)), Pchar(StrToAnsi(windowcaption)), WS_Caption or WS_POPUPWINDOW or WS_VISIBLE, (screen.Width - Window_W - 2 * Engine_BW) div 2, (screen.Height - Window_H - Engine_CH - 2 * Engine_BH) div 2, Window_W + 2 * Engine_BW, Window_H + Engine_CH + Engine_BH * 2, 0, 0, hInstance, nil);
  end
  else
    GameWindow := CreateWindowEx(0, Pchar(StrToAnsi(lpszClassName)), Pchar(StrToAnsi(windowcaption)), WS_SYSMENU or WS_POPUP or WS_VISIBLE, 0, 0, Window_W, Window_H, 0, 0, hInstance, nil);
  //GameWindow := CreateWindow(Pwidechar(widestring(lpszClassName)), Pwidechar(widestring(windowcaption)), WS_OVERLAPPED or WS_CAPTION or WS_THICKFRAME, 0, 0, Window_W, Window_H, 0, 0, hInstance, nil);
  if gameWindow = 0 then
  begin
    GameDebug.AddDebugInfo('Create Game Window Fail!');
    result := 2;
    exit;
  end;
  showcursor(EngineShowCursor);
  CursorShow := EngineShowCursor;
  result := 0;
end;

//初始化DirectDraw
function TEngine.InitialDDraw: integer;
var
  SetClevel: Boolean;

begin

  GameDebug.AddDebugInfo('Create DirectDraw!');
  if DirectDrawCreateEx(nil, DDraw7, IDirectDraw7, nil) <> DD_OK then
  begin
    GameDebug.AddDebugInfo('Create DirectDraw Fail!');
    result := 2;
    exit;
  end;
 { if DirectDrawCreate(nil, DDraw, nil) <> DD_OK then
  begin
    result := 1;
    exit;
  end;
  if DDraw.QueryInterface(IID_IDirectDraw7, DDraw7) <> DD_OK then
  begin
    result := 2;
    exit;
  end;          }
  GameDebug.AddDebugInfo('Set Cooperative Level!');
  if WMode then
    SetClevel := (DDraw7.SetCooperativeLevel(GameWindow, DDSCL_NORMAL) <> DD_OK)
  else
    SetClevel := (DDraw7.SetCooperativeLevel(GameWindow, DDSCL_ALLOWREBOOT or DDSCL_ALLOWMODEX or DDSCL_FULLSCREEN or DDSCL_EXCLUSIVE) <> DD_OK);
  if SetCLevel then
  begin
    GameDebug.AddDebugInfo('Set Cooperative Level Fail!');
    result := 3;
    exit;
  end;

  if not WMode then
  begin
    GameDebug.AddDebugInfo('Set Display Mode!');
    if DDraw7.SetDisplayMode(window_w, window_h, dwpp, 0, 0) <> DD_OK then
    begin
      GameDebug.AddDebugInfo('Set Display Mode Fail!');
      result := 4;
      exit;
    end;
  end;

  ZeroMemory(@ddsd, sizeof(ddsd));
  ddsd.dwSize := sizeof(ddsd);
  if WMode then
  begin
    //ddsd.ddpfPixelFormat.dwRGBBitCount := 32;
    ddsd.dwFlags := DDSD_CAPS;
    ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE;
  end
  else
  begin
    ddsd.dwFlags := DDSD_CAPS;// or DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE;// or DDSCAPS_FLIP;
    //ddsd.dwBackBufferCount := 1;
  end;
  GameDebug.AddDebugInfo('Cteate Screen Surface!');
  if DDraw7.CreateSurface(ddsd, ScreenSurface, nil) <> DD_OK then
  begin
    GameDebug.AddDebugInfo('Cteate Screen Surface Fail!');
    result := 5;
    exit;
  end;
  ZeroMemory(@Bufddsd, sizeof(Bufddsd));
  Bufddsd.dwSize := sizeof(Bufddsd);
  Bufddsd.dwFlags := DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT;

  if AUTOSETVIDEO or (VIDEOMODE = 0) then
  begin

    if VersionInformation.dwMajorVersion >= 6 then
    begin
      Bufddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_SYSTEMMEMORY;
    end
    else
    begin
      Bufddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_VIDEOMEMORY or DDSCAPS_NONLOCALVIDMEM;
    end;
  end
  else
  begin
    if VIDEOMODE = 1 then
    begin
      Bufddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_SYSTEMMEMORY;
    end
    else if VIDEOMODE = 2 then
    begin
      Bufddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_VIDEOMEMORY or DDSCAPS_NONLOCALVIDMEM;
    end
    else if VIDEOMODE = 3 then
    begin
      Bufddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_VIDEOMEMORY or DDSCAPS_LOCALVIDMEM;
    end
    else
    begin
      Bufddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_SYSTEMMEMORY;
    end;
  end;

  Bufddsd.dwWidth := Window_W;
  Bufddsd.dwHeight := Window_H;
  GameDebug.AddDebugInfo('Cteate Screen Buffer Surface!');
  if DDraw7.CreateSurface(bufddsd, ScreenBufSurface, nil) <> DD_OK then
  begin
    GameDebug.AddDebugInfo('Cteate Screen Buffer Surface Fail!');
    result := 5;
    exit;
  end;

  if wMode then
  begin
    GameDebug.AddDebugInfo('Create DirectDraw Clipper!');
    if DDraw7.CreateClipper(0, DdClipper, nil) <> DD_OK then
    begin
      GameDebug.AddDebugInfo('Create DirectDraw Clipper Fail!');
      result := 6;
      exit;
    end;
    GameDebug.AddDebugInfo('Set Clipper!');
    if not ((DdClipper.SetHWnd(0, GameWindow) = DD_OK) and (ScreenSurface.SetClipper(DdClipper) = DD_OK)) then
    begin
      GameDebug.AddDebugInfo('Set Clipper Fail!');
      result := 7;
      exit;
    end;

    ShowWindow(GameWindow, SW_SHOWDEFAULT);
    UpdateWindow(GameWindow);
    //
    GameDebug.AddDebugInfo('Show Window!');
  end;

  ScreenBufLock;
  ScreenLpitch := bufddsd.lPitch;
  ScreenBufSize := Window_H * ScreenLpitch;
  setlength(ScreenData, ScreenBufSize);
  ScreenBufUnLock;

  result := 0;
end;

function TEngine.CreateNewSurface(W, H: integer; var Psurface: PDirectDrawSurface7; var Newdesc: DDSURFACEDESC2): integer;
var
  TempDDSD: DDSURFACEDESC2;
  NewSurface: IDirectDrawSurface7;
begin
  //
  Psurface := nil;
  ZeroMemory(@tempddsd, sizeof(tempddsd));
  tempddsd.dwSize := sizeof(tempddsd);
  tempddsd.dwFlags := DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT;
  tempddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN;
  tempddsd.dwWidth := Window_W;
  tempddsd.dwHeight := Window_H;
  if DDraw7.CreateSurface(tempddsd, NewSurface, nil) <> DD_OK then
  begin
    result := 2;
    exit;
  end;
  Psurface := @NewSurface;
  NewSurface.GetSurfaceDesc(NewDesc);
  InitialAlpha(@NewDesc);
  result := 0;
end;

procedure TEngine.Restore(Psurface: PDirectDrawSurface7);
begin
  Psurface._Restore;
end;

procedure TEngine.SurfaceRestore;
begin
  ScreenBufSurface._Restore;
  ScreenSurface._Restore;
end;

//释放DirectDraw
procedure TEngine.ReleaseDDraw;
begin
  GameDebug.AddDebugInfo('Release DirectDraw!');
  //DdClipper._Release;

  ScreenBufSurface._Release;

  ScreenSurface._Release;

  if WMode then
    DdClipper._Release;

  DDraw7._Release;
end;

//释放一个surface
procedure TEngine.ReleaseSurface(Psurface: PDirectDrawSurface7);
begin
  Psurface._Release;
end;

procedure TEngine.initialalpha(DirectDsd: PDDSURFACEDESC2);
begin
  fillchar(DirectDsd.lpSurface^, $FF, DirectDsd.dwHeight * DirectDsd.lPitch);
end;

function TEngine.BufTextOut(Str: String; x, y, Size, Col: integer): integer;
var
  DC: HDC;
  StrFont, OldFont: HFont;
  tempStr: Ansistring;
begin
  //
  if ScreenBufSurface.GetDC(DC) <> DD_OK then
  begin
    result := 1;
    exit;
  end;
  StrFont := CreateFont(Size, 0, 0, 0, FW_THIN, 0, 0, 0, GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH or FF_SWISS, '宋体');
  OldFont := SelectObject(dc, StrFont);
  SetBkMode(DC, TRANSPARENT);
  SetTextColor(dc, Col);
  tempStr := StrToAnsi(str);
  TextOut(dc, x, y, Pchar(tempStr), length(tempStr));
  SelectObject(DC, OldFont);
  DeleteObject(StrFont);
  ScreenBufSurface.ReleaseDC(DC);
  result := 0;
end;

//画像素
procedure TEngine.putpixel(DirectDsd: PDDSURFACEDESC2; x, y: integer; Color: Cardinal);
var
  lpBitmap: Pbyte;
begin
  //
  //lpBitmap := Directdsd.lpSurface;
  lpBitmap := Directdsd.lpSurface + y * Directdsd.lPitch + x shl 2;
  PCardinal(lpBitmap)^ := color;
end;

procedure TEngine.putpixel(x, y: integer; Color: Cardinal);
begin
  //
  putpixel(@ddsd, x, y, color);
end;

procedure TEngine.putBufpixel(x, y: integer; Color: Cardinal);
var
  lpBitmap: Pbyte;
begin
  //
  //putpixel(@bufddsd, x, y, color);
  lpBitmap := bufddsd.lpSurface + y * bufddsd.lPitch + x shl 2;
  PCardinal(lpBitmap)^ := color;
end;

procedure TEngine.putdatapixel(x, y: integer; Color: Cardinal);
begin
  Pcardinal(@ScreenData[y * ScreenLpitch + x * dwpp shr 3])^ := color;
end;

//取得像素颜色
function TEngine.getpixel(DirectDsd: PDDSURFACEDESC2; x, y: integer): Cardinal;
var
  lpBitmap: Pbyte;
begin
  lpBitmap := Directdsd.lpSurface + y * Directdsd.lPitch + x shl 2;
  result := PCardinal(lpBitmap)^;
end;

function TEngine.getpixel(x, y: integer): Cardinal;
begin
  result := getpixel(@ddsd, x, y);
end;

function TEngine.getBufpixel(x, y: integer): Cardinal;
var
  lpBitmap: Pbyte;
begin
  //result := getpixel(@bufddsd, x, y);
  lpBitmap := bufddsd.lpSurface + y * bufddsd.lPitch + x shl 2;
  result := PCardinal(lpBitmap)^;
end;

function TEngine.ScreenLock: integer;
begin
  if NeedRestore then
  begin
    SurfaceRestore;
    needRestore := false;
  end;
  result := ScreenSurface.Lock(nil, ddsd, DDLOCK_WAIT or DDLOCK_SURFACEMEMORYPTR, 0);
end;

function TEngine.ScreenUnLock: integer;
begin
  result := ScreenSurface.UnLock(nil);
end;

function TEngine.ScreenBufLock: integer;
begin
  if NeedRestore then
  begin
    SurfaceRestore;
    needRestore := false;
  end;
  result := ScreenBufSurface.Lock(nil, Bufddsd, DDLOCK_WAIT or DDLOCK_SURFACEMEMORYPTR, 0);
end;

function TEngine.ScreenBufUnLock: integer;
begin
  result := ScreenBufSurface.UnLock(nil);
  //messagebox(0, Pchar(inttostr(integer(ScreenBufSurface))), Pchar(inttostr(result)), 0);
end;

function TEngine.UpdateCursor(var x, y: integer): Boolean;
begin
  //
  if WMode then
  begin
    if Engine_Active and (x >= Engine_Crect.Left) and (y >= Engine_Crect.Top) and (x < Engine_Crect.Right) and (y < Engine_Crect.Bottom)  then
    begin
      if CursorShow and not EngineShowCursor then
      begin
        showcursor(false);
        CursorShow := false;
      end;
      result := false;
      x := x - Engine_Crect.Left;
      y := y - Engine_Crect.Top;
    end
    else
    begin
      if not CursorShow and not EngineShowCursor then
      begin
        showcursor(true);
        CursorShow := true;
      end;
      result := true;
      //x := x - Crect.Left;
      //y := y - Crect.Top;
      x := -1;
      y := -1;
    end;
  end
  else
    result := false;
end;

procedure TEngine.DataToBuf;
begin
  copymemory(bufddsd.lpSurface, @ScreenData[0], ScreenBufSize);
end;

procedure TEngine.BufToData;
begin
  copymemory(@ScreenData[0], bufddsd.lpSurface, ScreenBufSize);
end;

procedure TEngine.drawpictobuf(APIC: PPicture; x, y: integer);
var
  DC: HDC;
  cvs: TCanvas;
begin
  //
  try
    cvs := TCanvas.create;

    if ScreenBufSurface.GetDC(DC) = DD_OK then
    begin
      cvs.Handle:=DC;
      cvs.draw(x, y, APIC.Graphic);
      ScreenBufSurface.ReleaseDC(DC);
    end;

  finally
    cvs.free;
  end;
end;

procedure TEngine.drawpictobuf(Pdata: Pbyte; datalen, x, y: integer);
var
  rs: TmemoryStream;
  PIC: TPicture;
  DC: HDC;
  cvs: TCanvas;
begin
  try
    rs := TmemoryStream.Create;
    PIC := TPicture.Create;
    if datalen > 0 then
    begin
      rs.SetSize(datalen);
      rs.Position := 0;
      rs.Write(Pdata^, datalen);
      rs.Position := 0;
      PIC.LoadFromStream(rs);

      drawpictobuf(@PIC, x, y);

    end;
  finally
    PIC.Free;
    rs.Free;
  end;
end;

procedure TEngine.drawpictobuf(Filename: String; x, y: integer);
var
  rs: TmemoryStream;
  PIC: TPicture;
  DC, TempDC: HDC;

begin
  if not fileexists(Filename) then
    exit;
  try
    PIC := TPicture.Create;
    PIC.LoadFromFile(FileName);
    drawpictobuf(@PIC, x, y);

  finally
    PIC.Free;
  end;

end;

procedure TEngine.Update;
var
  DF: TDDBltFX;
begin

  ScreenbufUnlock;

  if WMode then
  begin
    Zeromemory(@DF, sizeof(DF));
    DF.dwSize := SizeOf(DF);
    DF.dwDDFX := 0;
    ScreenSurface.Blt(@Engine_Crect, ScreenBufSurface, nil, DDBLT_WAIT, @DF);
  end
  else
    ScreenSurface.BltFast(0, 0, ScreenBufSurface, nil, DDBLTFAST_WAIT);

end;

end.
